#include "trace_def.h"
#include "string_util.h"
#include <iterator>
#include <algorithm>
using namespace std;

// Generate function trace clause
class ProcFunc {
    ostream& stream;
    const string& exclude;
    const string& module;
public:
    ProcFunc(ostream& st, const string& ex, const string& mod) :
        stream(st), exclude(ex), module(mod) {}

    void operator()(string func) {
        // entry
        stream << "pid$target:" << module << ":" << func << ":entry";
        if (exclude.length() > 0) {
            stream << endl << exclude;
        }
        stream << endl
               << "{\n"
                  "    printf(\"->%s    %d    %s\\n\", probefunc, timestamp, probemod);\n"
                  "    @count[probefunc] = count();\n"
                  "}\n\n"; 

        // return
        stream << "pid$target:" << module << ":" << func << ":return";
        if (exclude.length() > 0) {
            stream << endl << exclude;
        }
        stream << endl
               << "{\n"
                  "    printf(\"<-%s    %d    %s\\n\", probefunc, timestamp, probemod);\n"
                  "}\n\n"; 
    }
};

ostream& operator<<(ostream& stream, TraceItem& item) {
    vector<string>::const_iterator iter;
    // build up the exclude predicate
    string excludelist("/");
    if (item.exclude.size() > 0) {
        for (iter = item.exclude.begin();
                iter != item.exclude.end();
                ++iter) {
            excludelist += "probefunc != \"";
            excludelist += *iter;
            excludelist += "\" && ";
        }
        excludelist.erase(excludelist.length() - 4);
        excludelist += "/";
    } else
        excludelist = "";

    ProcFunc procfunc = ProcFunc(stream, excludelist, item.module);
    if (item.function.size() > 0)
        for_each(item.function.begin(), item.function.end(), procfunc);
    else
        procfunc("");
    return stream;
}

int TraceItem::parse(string def, TraceItem& item) {
    vector<string> all = string_split(def, ":!");
    if (all.size() == 0 || all.size() > 3)
        return -1;

    item.module = all[0];

    if (all.size() > 1)
        item.function = string_split(all[1], ",");
    if (all.size() > 2)
        item.exclude = string_split(all[2], ",");
    return 0;
}
